package com.xyzwps.paimon.furina.modules.sms.vcode

import com.xyzwps.paimon.furina.common.value.Phone
import com.xyzwps.paimon.furina.common.value.Vcode
import com.xyzwps.paimon.furina.modules.sms.service.SmsService
import org.redisson.api.RedissonClient
import org.springframework.stereotype.Service
import java.util.concurrent.TimeUnit

@Service
class VcodeService(
    private val smsService: SmsService,
    private val redisson: RedissonClient,
    vcodeContextRepository: VcodeContextRepository,
) {
    private val vcodeManager = VcodeManager(
        vcodeContextRepository = vcodeContextRepository,
        vcodeExpireSeconds = 5 * 60,
        vcodeGapSeconds = 60,
        vcodeMaxCheckTimes = 3,
        contextHistoryLength = 5,
        sendVcodeFn = { phone, vcode, scene -> smsService.sendVcode(phone, scene, vcode) }
    )

    fun sendVcode(phone: Phone, scene: VcodeScene) {
        withLock(phone, scene) { vcodeManager.send(phone, Vcode.randomVcode(), scene) }
    }

    fun checkVcode(phone: Phone, scene: VcodeScene, vcode: Vcode) {
        withLock(phone, scene) { vcodeManager.check(phone, vcode, scene) }
    }

    private fun withLock(phone: Phone, scene: VcodeScene, fn: () -> Unit) {
        val lock = getLock(phone, scene)
        if (lock.tryLock(5, TimeUnit.SECONDS)) {
            try {
                fn()
            } finally {
                if (lock.isLocked) {
                    lock.unlock()
                }
            }
        }
    }

    private fun getLock(phone: Phone, scene: VcodeScene) =
        redisson.getFairLock("furina:vcode-send-lock:scene:$scene:phone:${phone.value}")
}


